#ifndef _DArray_h
#define _DArray_h
#include <stdlib.h>
#include <assert.h>
#include <lcthw/dbg.h>

typedef struct DArray
{
  int end;             // The number of elements in the array
  int max;             // The number of elements the array can hold
  size_t element_size; // The size of each element in the array
  size_t expand_rate;  // The rate at which the array expands
  void **contents;     // pointer to the array of darray elements
} DArray;

DArray *DArray_create(size_t element_size, size_t initial_max);

void DArray_destroy(DArray *array);

void DArray_clear(DArray *array);

int DArray_expand(DArray *array); // Expands the array  by the expand_rate

int DArray_contract(DArray *array); // Contracts the array by the expand_rate

int DArray_push(DArray *array, void *el); // Adds an element to the end of the array

void *DArray_pop(DArray *array); // Removes the last element from the array

void DArray_clear_destroy(DArray *array);

#define DArray_last(A) ((A)->contents[(A)->end - 1]) // Returns the last element in the array
#define DArray_first(A) ((A)->contents[0])           // Returns the first element in the array
#define DArray_end(A) ((A)->end)                     // Returns the number of elements in the array
#define DArray_count(A) DArray_end(A)                // Returns the number of elements in the array
#define DArray_max(A) ((A)->max)                     // Returns the number of elements the array can hold

#define DEFAULT_EXPAND_RATE 300

static inline void DArray_set(DArray *array, int i, void *el) // Sets the element at the index i
{
  check(i + 1 < array->max, "darray attempt to set past max");
  if (i + 1 > array->end)
  {
    array->end = i + 1;
  }
  array->contents[i] = el;
error:
  return;
}

static inline void *DArray_get(DArray *array, int i) // Returns the element at the index i
{
  check(i + 1 < array->max, "darray attempt to get past max");
  return array->contents[i];
error:
  return NULL;
}

static inline void *DArray_remove(DArray *array, int i) // Removes an element from the array
{
  void *el = array->contents[i];
  array->contents[i] = NULL;
  return el;
}

static inline void *DArray_new(DArray *array) // Allocates memory for a new element, but has not added it to the array
{
  check(array->element_size > 0, "Can't use DArray_new on 0 size darrays.");
  return calloc(1, array->element_size);
error:
  return NULL;
}

#define DArray_free(E) free((E))

#endif